// Preference module, added by regret

/* changelog:
 update 3: modeless property sheet
 update 2: add property sheet dialog
 update 1: create
*/

#include "burner.h"
#include "preference.h"
#include "vid_filter.h"

HWND hPrefDlg = NULL;
static int posx, posy;

// ----------------------------------------------------------------------------
// Video config
static HWND hVideoDlg = NULL;

static int dispApapterInit()
{
	if (!hVideoDlg) {
		return 1;
	}

	unsigned int count = 0;
	int ret = 0;
	DISPLAY_DEVICE dev;
	memset(&dev, 0, sizeof(dev));
	dev.cb = sizeof(dev);

	do {
		ret = EnumDisplayDevices(NULL, count, &dev, 0);
		if (ret) {
			if (dev.StateFlags & DISPLAY_DEVICE_ACTIVE) {
				SendDlgItemMessage(hVideoDlg, IDC_PREF_DISPADAPTER, CB_ADDSTRING, 0, (LPARAM)dev.DeviceName);
			}
			count++;
		}
	} while (ret);

	SendDlgItemMessage(hVideoDlg, IDC_PREF_DISPADAPTER, CB_SETCURSEL, (WPARAM)vidAdapter, 0);

	return 0;
}

static int videoModeInit()
{
	if (!hVideoDlg) {
		return 1;
	}

	const wchar_t* list = videoInterface.driver_list();

	// audio output string list
	wchar_t szToken[MAX_PATH];
	wcscpy(szToken, list);
	wchar_t* token = wcstok(szToken, L";");
	int index = 0;
	while (token) {
		SendDlgItemMessage(hVideoDlg, IDC_PREF_VIDEOMODE, CB_ADDSTRING, 0, (LPARAM)token);
		// set selected audio output
		if (!wcscmp(vidSelect, token)) {
			SendDlgItemMessage(hVideoDlg, IDC_PREF_VIDEOMODE, CB_SETCURSEL, (WPARAM)index, 0);
		}
		token = wcstok(NULL, L";");
		index++;
	}
	free(token);

	return 0;
}

static int fullResInit()
{
	if (!hVideoDlg) {
		return 1;
	}

	DEVMODE devMode;
	devMode.dmSize = sizeof(DEVMODE);
	devMode.dmDriverExtra = 0;

	DISPLAY_DEVICE device;
	device.cb = sizeof(DISPLAY_DEVICE);
	EnumDisplayDevices(NULL, vidAdapter, &device, 0);

	// fullscreen res box
	// Run through all possible screenmodes and fill the combobox
	for (int i = 0; EnumDisplaySettings(device.DeviceName, i, &devMode); i++) {
		// We're only interested in 16, 24, or 32 bit modes.
		if (devMode.dmBitsPerPel & 0x30) {
			long item = 0, itemValue;
			long newRes = (devMode.dmPelsWidth << 16) | devMode.dmPelsHeight;

			// See if the resolution is already in the combobox
			do {
				itemValue = SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_GETITEMDATA, item, 0);
				item++;
			} while ((itemValue != CB_ERR) && (itemValue != newRes));

			// If not, add it
			if (itemValue == CB_ERR) {
				wchar_t szRes[MAX_PATH];
				swprintf(szRes, L"%li x %li", devMode.dmPelsWidth, devMode.dmPelsHeight);
				item = SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_ADDSTRING, 0, (LPARAM)&szRes);
				SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_SETITEMDATA, item, newRes);
			}
		}
	}

	// set select
	long myRes = (vidWidth << 16) | vidHeight;
	long count = SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_GETCOUNT, 0, 0);
	long item = 0;
	for (long i = 0; i < count; i++) {
		item = SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_GETITEMDATA, i, 0);
		if (myRes == item) {
			SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_SETCURSEL, (WPARAM)i, 0);
			break;
		}
	}

	// depth
	SendDlgItemMessage(hVideoDlg, IDC_PREF_16BIT, BM_SETCHECK, (vidDepth == 16) ? BST_CHECKED : BST_UNCHECKED, 0);
	SendDlgItemMessage(hVideoDlg, IDC_PREF_32BIT, BM_SETCHECK, (vidDepth == 32) ? BST_CHECKED : BST_UNCHECKED, 0);

	return 0;
}

static void videoOptionInit()
{
	if (!hVideoDlg) {
		return;
	}

	CheckDlgButton(hVideoDlg, IDC_PREF_COLOR_ADJUST, colorAdjustEnable ? BST_CHECKED : BST_UNCHECKED);

	CheckDlgButton(hVideoDlg, IDC_PREF_TRIPLE, vidTripleBuffer ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_VSYNC, vidVSync ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_FORCE60HZ, burnForce60Hz ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_AUTOFRAMESKIP, autoFrameSkip ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_BLITTERPREVIEW, vidUsePlaceholder ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_AUTOMONITORASPECT, vidAutoScrnAspect ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_MONITORMIRRORVERT, (vidRotationAdjust & 2) ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hVideoDlg, IDC_PREF_ROTATEVERTICAL, (vidRotationAdjust & 1) ? BST_CHECKED : BST_UNCHECKED);

	EnableWindow(GetDlgItem(hVideoDlg, IDC_PREF_SETCOLOR), colorAdjustEnable);
	EnableWindow(GetDlgItem(hVideoDlg, IDC_PREF_CUSTOMMONITORASPECT), !vidAutoScrnAspect);
	EnableWindow(GetDlgItem(hVideoDlg, IDC_PREF_FORCE60HZ), !driverOkay);
	EnableWindow(GetDlgItem(hVideoDlg, IDC_PREF_BLITTERPREVIEW), !driverOkay);

	// scanline intensity
	wchar_t szText[32];
	_snwprintf(szText, sizearray(szText), L"%d%%", scanlineFilter.intensity);
	SetDlgItemText(hVideoDlg, IDC_SCANLINE_TEXT, szText);
	SendDlgItemMessage(hVideoDlg, IDC_PREF_SCANLINE, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(0, 100));
	SendDlgItemMessage(hVideoDlg, IDC_PREF_SCANLINE, TBM_SETPAGESIZE, 0, (LPARAM)10);
	SendDlgItemMessage(hVideoDlg, IDC_PREF_SCANLINE, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)scanlineFilter.intensity);
}

static void videoModeChange()
{
	if (!hVideoDlg) {
		return;
	}

	int sel = SendDlgItemMessage(hVideoDlg, IDC_PREF_VIDEOMODE, CB_GETCURSEL, 0, 0);
	wchar_t output[MAX_PATH] = L"";
	SendDlgItemMessage(hVideoDlg, IDC_PREF_VIDEOMODE, CB_GETLBTEXT, (WPARAM)sel, (LPARAM)output);
	videoInterface.select(output);
	videoInterface.reinit();
}


static void prefVideoExit()
{
	if (!hVideoDlg) {
		return;
	}

	vidAdapter = SendDlgItemMessage(hVideoDlg, IDC_PREF_DISPADAPTER, CB_GETCURSEL, 0, 0);

	// fullscreen resolution
	long resItem = SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_GETCURSEL, 0, 0);
	long itemValue = SendDlgItemMessage(hVideoDlg, IDC_PREF_FULLSCREENRES, CB_GETITEMDATA, resItem, 0);
	vidWidth = itemValue >> 16;
	vidHeight = itemValue & 0xFFFF;

	if (BST_CHECKED == IsDlgButtonChecked(hVideoDlg, IDC_PREF_16BIT)) {
		vidDepth = 16;
	} else if (BST_CHECKED == IsDlgButtonChecked(hVideoDlg, IDC_PREF_32BIT)) {
		vidDepth = 32;
	}

	burnForce60Hz = (BST_CHECKED == IsDlgButtonChecked(hVideoDlg, IDC_PREF_FORCE60HZ)) ? 1 : 0;
}

static INT_PTR CALLBACK prefVideoDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_INITDIALOG:
			wndInMid(hDlg, hScrnWnd);
			hVideoDlg = hDlg;

			// init controls
			videoModeInit();
			dispApapterInit();
			fullResInit();
			videoOptionInit();

			return TRUE;

		case WM_COMMAND: {
			int Id = LOWORD(wParam);
			int Notify = HIWORD(wParam);

			if ((HWND)lParam == GetDlgItem(hDlg, IDC_PREF_VIDEOMODE)) {
				if (Notify == CBN_SELCHANGE) {
					videoModeChange();
				}
			}

			if ((Id == IDOK && Notify == BN_CLICKED) || (Id == IDCANCEL && Notify == BN_CLICKED)) {
				SendMessage(hDlg, WM_CLOSE, 0, 0);
				return 0;
			}
			else if (Id == IDC_PREF_ROTATEVERTICAL && Notify == BN_CLICKED) {
				vidRotationAdjust ^= 1;
				if (driverOkay) {
					simpleReinitScrn(true);
				}
				return 0;
			}
			else if (Id == IDC_PREF_MONITORMIRRORVERT && Notify == BN_CLICKED) {
				vidRotationAdjust ^= 2;
				if (driverOkay) {
					simpleReinitScrn(true);
				}
				return 0;
			}
			else if (Id == IDC_PREF_TRIPLE && Notify == BN_CLICKED) {
				vidTripleBuffer = (BST_CHECKED == IsDlgButtonChecked(hDlg, Id)) ? 1 : 0;
				videoInterface.reinit();
				return 0;
			}
			else if (Id == IDC_PREF_VSYNC && Notify == BN_CLICKED) {
				vidVSync = (BST_CHECKED == IsDlgButtonChecked(hDlg, Id)) ? 1 : 0;
				return 0;
			}
			else if (Id == IDC_PREF_AUTOFRAMESKIP && Notify == BN_CLICKED) {
				autoFrameSkip = (BST_CHECKED == IsDlgButtonChecked(hDlg, Id)) ? 1 : 0;
				return 0;
			}
			else if (Id == IDC_PREF_BLITTERPREVIEW && Notify == BN_CLICKED) {
				vidUsePlaceholder = (BST_CHECKED == IsDlgButtonChecked(hDlg, Id)) ? 1 : 0;
				videoInterface.reinit();
				videoInterface.redraw();
				videoInterface.paint(0);
				return 0;
			}
			else if (Id == IDC_PREF_CUSTOMMONITORASPECT && Notify == BN_CLICKED) {
				aspectSetDialog(hDlg);
				setWindowAspect();
				simpleReinitScrn(true);
				return 0;
			}
			else if (Id == IDC_PREF_AUTOMONITORASPECT && Notify == BN_CLICKED) {
				BOOL checked = (BST_CHECKED == IsDlgButtonChecked(hDlg, Id));
				EnableWindow(GetDlgItem(hDlg, IDC_PREF_CUSTOMMONITORASPECT), !checked);
				vidAutoScrnAspect = checked;
				setWindowAspect();
				simpleReinitScrn(true);
				return 0;
			}
			else if (Id == IDC_PREF_SETCOLOR && Notify == BN_CLICKED) {
				colorAdjustDialog(hDlg);
				return 0;
			}
			else if (Id == IDC_PREF_COLOR_ADJUST && Notify == BN_CLICKED) {
				BOOL checked = (BST_CHECKED == IsDlgButtonChecked(hDlg, Id));
				EnableWindow(GetDlgItem(hDlg, IDC_PREF_SETCOLOR), checked);
				colorAdjustEnable = checked;
				videoInterface.reinit();
				if (!driverOkay) {
					videoInterface.redraw();
					videoInterface.paint(0);
				}
				return 0;
			}

			if (Notify == CBN_SELCHANGE) {
				if ((HWND)lParam == GetDlgItem(hDlg, IDC_PREF_DISPADAPTER)) {
					// reinit fullscreen resolution if adapter changed
					vidAdapter = SendDlgItemMessage(hDlg, IDC_PREF_DISPADAPTER, CB_GETCURSEL, 0, 0);
					fullResInit();
				}
			}
		}
		break;

		case WM_NOTIFY: {
			switch (((NMHDR *)lParam)->code) {
				case PSN_APPLY:
					prefVideoExit();
					break;
			}
		}
		break;

		case WM_HSCROLL: {
			switch (LOWORD(wParam)) {
				case TB_BOTTOM:
				case TB_ENDTRACK:
				case TB_LINEDOWN:
				case TB_LINEUP:
				case TB_PAGEDOWN:
				case TB_PAGEUP:
				case TB_THUMBPOSITION:
				case TB_THUMBTRACK:
				case TB_TOP: {
					wchar_t szText[32];
					unsigned intensity = SendDlgItemMessage(hDlg, IDC_PREF_SCANLINE, TBM_GETPOS, 0, 0);
					_snwprintf(szText, sizearray(szText), L"%d%%", intensity);
					SetDlgItemText(hDlg, IDC_SCANLINE_TEXT, szText);
					scanlineFilter.setIntensity(intensity);

					// Update the screen
					if (vidOkay) {
						videoInterface.redraw();
						videoInterface.paint(0);
					}
					break;
				}
			}
			break;
		}

		case WM_CLOSE:
			prefVideoExit();

			EndDialog(hDlg, 0);
			hVideoDlg = NULL;
			break;
	}

	return 0;
}

void prefVideoCreate(HWND parent)
{
	localiseDialogBox(IDD_PREF_VIDEO, parent, (DLGPROC)prefVideoDlgProc);
}

// ----------------------------------------------------------------------------
// Audio config

static HWND hAudioDlg = NULL;

static int audioOutputInit()
{
	if (!hAudioDlg) {
		return 1;
	}

	const wchar_t* list = audioInterface.driver_list();

	// audio output string list
	wchar_t szToken[MAX_PATH];
	wcscpy(szToken, list);
	wchar_t* token = wcstok(szToken, L";");
	int index = 0;
	while (token) {
		SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIOOUTPUT, CB_ADDSTRING, 0, (LPARAM)token);
		// set selected audio output
		if (!wcscmp(audSelect, token)) {
			SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIOOUTPUT, CB_SETCURSEL, (WPARAM)index, 0);
		}
		token = wcstok(NULL, L";");
		index++;
	}
	free(token);

	return 0;
}

static int audioDeviceInit()
{
	InterfaceInfo* info = audioInterface.get();
	if (!info || !hAudioDlg) {
		return 1;
	}

	SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIODEVICE, CB_RESETCONTENT, 0, 0);
	for (unsigned int i = 0; i < info->deviceNum; i++) {
		SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIODEVICE, CB_ADDSTRING, 0, (LPARAM)info->deviceName[i]);
	}
	SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIODEVICE, CB_SETCURSEL, (WPARAM)audioInterface.getdevice(), 0);

	return 0;
}

static void audioSampleInit()
{
	if (!hAudioDlg) {
		return;
	}

	SendDlgItemMessage(hAudioDlg, IDC_PREF_SAMPLERATE, CB_ADDSTRING, 0, (LPARAM)L"11025Hz");
	SendDlgItemMessage(hAudioDlg, IDC_PREF_SAMPLERATE, CB_ADDSTRING, 0, (LPARAM)L"22050Hz");
	SendDlgItemMessage(hAudioDlg, IDC_PREF_SAMPLERATE, CB_ADDSTRING, 0, (LPARAM)L"44100Hz");
	SendDlgItemMessage(hAudioDlg, IDC_PREF_SAMPLERATE, CB_ADDSTRING, 0, (LPARAM)L"48000Hz");

	int index = 0;
	if (audSampleRate > 0) {
		if (audSampleRate <= 11025) {
			index = 0;
		}
		else if (audSampleRate <= 22050) {
			index = 1;
		}
		else if (audSampleRate <= 44100) {
			index = 2;
		}
		else if (audSampleRate <= 48000) {
			index = 3;
		}
		else {
			index = 3;
		}
	}
	SendDlgItemMessage(hAudioDlg, IDC_PREF_SAMPLERATE, CB_SETCURSEL, (WPARAM)index, 0);

	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_SAMPLERATE), !driverOkay);
}

static void audioOptionInit()
{
	if (!hAudioDlg) {
		return;
	}

	CheckDlgButton(hAudioDlg, IDC_PREF_INTERPOLATE_1, (pcmInterpolation == 1) ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hAudioDlg, IDC_PREF_INTERPOLATE_3, (pcmInterpolation == 3) ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hAudioDlg, IDC_PREF_INTERPOLATE_FM_3, (fmInterpolation == 3) ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hAudioDlg, IDC_PREF_LOWPASS, (audDSPModule & 1) ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hAudioDlg, IDC_PREF_STEREOUPMIX, audStereoUpmixing ? BST_CHECKED : BST_UNCHECKED);

	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_INTERPOLATE_1), !driverOkay);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_INTERPOLATE_3), !driverOkay);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_INTERPOLATE_FM_3), !driverOkay);

	int xaudio2 = wcscmp(L"XAudio2", audSelect);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_STEREOUPMIX), (xaudio2 == 0) ? TRUE : FALSE);
}

static void audioFrameInit()
{
	if (!hAudioDlg) {
		return;
	}

	// Initialise slider
	wchar_t szText[MAX_PATH];
	_snwprintf(szText, sizearray(szText), localiseStringEx(IDS_NUMDLG_FRAME), audSegCount, int(1000.0 * (audSegCount - 1.0) / 60));
	SetDlgItemText(hAudioDlg, IDC_PREF_AUDFRAME_TEXT, szText);

	// limit in 2 - 10
	SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDFRAME, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(2, 10));
	SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDFRAME, TBM_SETTIC, 0, (LPARAM)6);
	SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDFRAME, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)audSegCount);
}

static void audioOutputChange()
{
	if (!hAudioDlg) {
		return;
	}

	// get selected audio output
	int sel = SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIOOUTPUT, CB_GETCURSEL, 0, 0);
	wchar_t output[MAX_PATH];
	SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIOOUTPUT, CB_GETLBTEXT, (WPARAM)sel, (LPARAM)output);

	// reinit audio
	audioInterface.select(output);
	mediaReInitAudio();

	// reinit device if output changed
	audioDeviceInit();

	int xaudio2 = wcscmp(L"XAudio2", output);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_STEREOUPMIX), (xaudio2 == 0) ? TRUE : FALSE);
}

static void prefAudioExit()
{
	if (!hAudioDlg) {
		return;
	}

	int index = SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDIODEVICE, CB_GETCURSEL, 0, 0);
	if (audioInterface.getdevice() != index) {
		audioInterface.setdevice(index);
	}

	index = SendDlgItemMessage(hAudioDlg, IDC_PREF_SAMPLERATE, CB_GETCURSEL, 0, 0);
	if (index == 0) {
		audSampleRate = 11025;
	}
	else if (index == 1) {
		audSampleRate = 22050;
	}
	else if (index == 2) {
		audSampleRate = 44100;
	}
	else {
		audSampleRate = 48000;
	}

	pcmInterpolation = (BST_CHECKED == IsDlgButtonChecked(hAudioDlg, IDC_PREF_INTERPOLATE_1)) ? 1 : 3;
	fmInterpolation = (BST_CHECKED == IsDlgButtonChecked(hAudioDlg, IDC_PREF_INTERPOLATE_FM_3)) ? 3 : 0;
	audDSPModule = (BST_CHECKED == IsDlgButtonChecked(hAudioDlg, IDC_PREF_LOWPASS)) ? 1 : 0;
	audStereoUpmixing = (BST_CHECKED == IsDlgButtonChecked(hAudioDlg, IDC_PREF_STEREOUPMIX)) ? 1 : 0;

	audSegCount = SendDlgItemMessage(hAudioDlg, IDC_PREF_AUDFRAME, TBM_GETPOS, 0, 0);

	mediaReInitAudio();
}

static INT_PTR CALLBACK prefAudioDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_INITDIALOG:
			wndInMid(hDlg, hScrnWnd);
			hAudioDlg = hDlg;

			// init controls
			audioOutputInit();
			audioDeviceInit();
			audioSampleInit();
			audioOptionInit();
			audioFrameInit();

			return TRUE;

		case WM_COMMAND: {
			int Id = LOWORD(wParam);
			int Notify = HIWORD(wParam);

			if ((Id == IDOK && Notify == BN_CLICKED) || (Id == IDCANCEL && Notify == BN_CLICKED)) {
				SendMessage(hDlg, WM_CLOSE, 0, 0);
				return 0;
			}

			if ((HWND)lParam == GetDlgItem(hDlg, IDC_PREF_AUDIOOUTPUT)) {
				if (Notify == CBN_SELCHANGE) {
					audioOutputChange();
				}
			}
		}
		break;

		case WM_HSCROLL:
			switch (LOWORD(wParam)) {
				case TB_BOTTOM:
				case TB_ENDTRACK:
				case TB_LINEDOWN:
				case TB_LINEUP:
				case TB_PAGEDOWN:
				case TB_PAGEUP:
				case TB_THUMBPOSITION:
				case TB_THUMBTRACK:
				case TB_TOP: {
					wchar_t szText[MAX_PATH];
					int frame = SendDlgItemMessage(hDlg, IDC_PREF_AUDFRAME, TBM_GETPOS, 0, 0);
					_snwprintf(szText, sizearray(szText), localiseStringEx(IDS_NUMDLG_FRAME), frame, int(1000.0 * (frame - 1.0) / 60));
					SetDlgItemText(hDlg, IDC_PREF_AUDFRAME_TEXT, szText);
					break;
				}
			}
			break;

		case WM_NOTIFY: {
			switch (((NMHDR *)lParam)->code) {
				case PSN_APPLY:
					prefAudioExit();
					break;
			}
		}
		break;

		case WM_CLOSE:
			prefAudioExit();

			EndDialog(hDlg, 0);
			hAudioDlg = NULL;
			break;
	}

	return 0;
}

void prefAudioCreate(HWND parent)
{
	localiseDialogBox(IDD_PREF_AUDIO, parent, (DLGPROC)prefAudioDlgProc);
}

// ----------------------------------------------------------------------------
// Misc config

static HWND hMiscDlg = NULL;

static void miscOptionInit()
{
	if (!hMiscDlg) {
		return;
	}

	CheckDlgButton(hMiscDlg, IDC_PREF_ONTOP, showOnTop ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hMiscDlg, IDC_PREF_AUTOPAUSE, autoPause ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hMiscDlg, IDC_PREF_PROCESSKEY, alwaysProcessKey ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hMiscDlg, IDC_PREF_AUTOFULLSCREEN, fullscreenOnStart ? BST_CHECKED : BST_UNCHECKED);

	CheckDlgButton(hMiscDlg, IDC_PREF_DISABLESPLASH, disableSplash ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hMiscDlg, IDC_PREF_RANDOMSKIN, randomSkin ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hMiscDlg, IDC_PREF_GDIPLUS, useGdip ? BST_CHECKED : BST_UNCHECKED);
}

static void miscSliderInit()
{
	if (!hMiscDlg) {
		return;
	}

	// Initialise slider
	wchar_t szText[16];
	_snwprintf(szText, sizearray(szText), L"%d", appThreadPriority);
	SetDlgItemText(hMiscDlg, IDC_PREF_THREADPRIORITY_TEXT, szText);

	// thread priority, limit in -15 ~ 1
	SendDlgItemMessage(hMiscDlg, IDC_PREF_THREADPRIORITY, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(-15, 1));
	SendDlgItemMessage(hMiscDlg, IDC_PREF_THREADPRIORITY, TBM_SETTIC, 0, 0);
	SendDlgItemMessage(hMiscDlg, IDC_PREF_THREADPRIORITY, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)appThreadPriority);
}

static void miscComboboxInit()
{
	if (!hMiscDlg) {
		return;
	}

#ifndef NO_IMAGEMENU
	for (int i = IDS_PREF_MENUNONE; i <= IDS_PREF_MENUEXTRA; i++) {
		SendDlgItemMessage(hMiscDlg, IDC_PREF_MENUSTYLE, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(i));
	}
	SendDlgItemMessage(hMiscDlg, IDC_PREF_MENUSTYLE, CB_SETCURSEL, (WPARAM)menuNewStyle, 0);
#endif

	for (int i = IDS_PREF_EFFECTNONE; i <= IDS_PREF_EFFECTTV; i++) {
		SendDlgItemMessage(hMiscDlg, IDC_PREF_EFFECTTYPE, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(i));
	}
	SendDlgItemMessage(hMiscDlg, IDC_PREF_EFFECTTYPE, CB_SETCURSEL, (WPARAM)showEffect, 0);

	wchar_t speedstr[32];
	for (int i = 2; i < 9; i++) {
		swprintf(speedstr, L"%d%%", i * 100);
		SendDlgItemMessage(hMiscDlg, IDC_PREF_FASTFORWARD, CB_ADDSTRING, 0, (LPARAM)speedstr);
	}
	SendDlgItemMessage(hMiscDlg, IDC_PREF_FASTFORWARD, CB_SETCURSEL, (WPARAM)fastSpeed - 1, 0);
}

static void prefMiscExit()
{
	if (!hMiscDlg) {
		return;
	}

	showOnTop = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_ONTOP)) ? 1 : 0;
	if (driverOkay && showOnTop) {
		SetWindowPos(hScrnWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOMOVE);
	}
	else if (!showOnTop) {
		SetWindowPos(hScrnWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOMOVE);
	}

	autoPause = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_AUTOPAUSE)) ? 1 : 0;
	alwaysProcessKey = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_PROCESSKEY)) ? 1 : 0;
	fullscreenOnStart = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_AUTOFULLSCREEN)) ? 1 : 0;

	disableSplash = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_DISABLESPLASH)) ? 1 : 0;
	randomSkin = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_RANDOMSKIN)) ? 1 : 0;
	useGdip = (BST_CHECKED == IsDlgButtonChecked(hMiscDlg, IDC_PREF_GDIPLUS)) ? 1 : 0;

#ifndef NO_IMAGEMENU
	menuNewStyle = SendDlgItemMessage(hMiscDlg, IDC_PREF_MENUSTYLE, CB_GETCURSEL, 0, 0);
#endif
	fastSpeed = SendDlgItemMessage(hMiscDlg, IDC_PREF_FASTFORWARD, CB_GETCURSEL, 0, 0) + 1;

	// set thread priority
	int priority = SendDlgItemMessage(hMiscDlg, IDC_PREF_THREADPRIORITY, TBM_GETPOS, 0, 0);
	if (appThreadPriority != priority) {
		appThreadPriority = priority;
		SetThreadPriority(GetCurrentThread(), appThreadPriority);
	}

	menuReinit();
}

static INT_PTR CALLBACK prefMiscDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_INITDIALOG:
			wndInMid(hDlg, hScrnWnd);
			hMiscDlg = hDlg;

			// init controls
			miscOptionInit();
			miscSliderInit();
			miscComboboxInit();

			return TRUE;

		case WM_COMMAND: {
			int Id = LOWORD(wParam);
			int Notify = HIWORD(wParam);

			if ((Id == IDOK && Notify == BN_CLICKED) || (Id == IDCANCEL && Notify == BN_CLICKED)) {
				SendMessage(hDlg, WM_CLOSE, 0, 0);
				return 0;
			}

			if ((HWND)lParam == GetDlgItem(hDlg, IDC_PREF_EFFECTTYPE)) {
				if (Notify == CBN_SELCHANGE) {
					showEffect = SendDlgItemMessage(hMiscDlg, IDC_PREF_EFFECTTYPE, CB_GETCURSEL, 0, 0);
					if (!driverOkay) {
						videoInterface.reinit();
					}
				}
			}
		}
		break;

		case WM_HSCROLL:
			switch (LOWORD(wParam)) {
				case TB_BOTTOM:
				case TB_ENDTRACK:
				case TB_LINEDOWN:
				case TB_LINEUP:
				case TB_PAGEDOWN:
				case TB_PAGEUP:
				case TB_THUMBPOSITION:
				case TB_THUMBTRACK:
				case TB_TOP: {
					wchar_t szText[16];
					int priority = SendDlgItemMessage(hDlg, IDC_PREF_THREADPRIORITY, TBM_GETPOS, 0, 0);
					_snwprintf(szText, sizearray(szText), L"%d", priority);
					SetDlgItemText(hDlg, IDC_PREF_THREADPRIORITY_TEXT, szText);
					break;
				}
			}
			break;

		case WM_NOTIFY: {
			switch (((NMHDR *)lParam)->code) {
				case PSN_APPLY:
					prefMiscExit();
					break;
			}
		}
		break;

		case WM_CLOSE:
			prefMiscExit();

			EndDialog(hDlg, 0);
			hMiscDlg = NULL;
			break;
	}

	return 0;
}

void prefMiscCreate(HWND parent)
{
	localiseDialogBox(IDD_PREF_MISC, parent, (DLGPROC)prefMiscDlgProc);
}

// Property sheet
void preferenceCreate(HWND parent)
{
	if (hPrefDlg) {
		SetActiveWindow(hPrefDlg);
		return;
	}

	HINSTANCE instance = localiseInstance();
	PROPSHEETPAGE psp[3];
	PROPSHEETHEADER psh;

	for (int i = 0; i < sizearray(psp); i++) {
		psp[i].dwSize = sizeof(PROPSHEETPAGE);
		psp[i].dwFlags = PSP_DEFAULT;
		psp[i].hInstance = instance;
		psp[i].lParam = 0;
		psp[i].pfnCallback = NULL;
	}
	psp[0].pszTemplate = MAKEINTRESOURCE(IDD_PREF_VIDEO);
	psp[0].pfnDlgProc = (DLGPROC)prefVideoDlgProc;
	psp[1].pszTemplate = MAKEINTRESOURCE(IDD_PREF_AUDIO);
	psp[1].pfnDlgProc = (DLGPROC)prefAudioDlgProc;
	psp[2].pszTemplate = MAKEINTRESOURCE(IDD_PREF_MISC);
	psp[2].pfnDlgProc = (DLGPROC)prefMiscDlgProc;

	psh.dwSize = sizeof(PROPSHEETHEADER);
	psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_MODELESS;
	psh.hwndParent = parent;
	psh.hInstance = instance;
	psh.pszCaption = localiseStringEx(IDS_PREF_SETTINGS);
	psh.nPages = sizearray(psp);
	psh.nStartPage = 0;
	psh.ppsp = (LPCPROPSHEETPAGE)&psp;
	psh.pfnCallback = NULL;

	hPrefDlg = (HWND)PropertySheet(&psh);
}

void preferenceExit()
{
	if (!hPrefDlg)
		return;

	RECT rect;
	GetWindowRect(hPrefDlg, &rect);
	posx = rect.left; posy = rect.top;

	DestroyWindow(hPrefDlg);
	hPrefDlg = NULL;
}

void preferenceEnable()
{
	if (!hPrefDlg)
		return;

	EnableWindow(GetDlgItem(hVideoDlg, IDC_PREF_FORCE60HZ), !driverOkay);
	EnableWindow(GetDlgItem(hVideoDlg, IDC_PREF_BLITTERPREVIEW), !driverOkay);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_SAMPLERATE), !driverOkay);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_INTERPOLATE_1), !driverOkay);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_INTERPOLATE_3), !driverOkay);
	EnableWindow(GetDlgItem(hAudioDlg, IDC_PREF_INTERPOLATE_FM_3), !driverOkay);
}

bool propertySheetMessage(MSG* msg)
{
	if (!hPrefDlg)
		return false;

	if (PropSheet_IsDialogMessage(hPrefDlg, msg)) {
		if (!PropSheet_GetCurrentPageHwnd(hPrefDlg)) {
			preferenceExit();
		}
		return true;
	}
	return false;
}
